let apis = {
    style: {
        getApp: {
            // url: 'http://127.0.0.1:3003/src/css/app.css',
            url: 'src/css/app.css',
            method: 'get'
        }
    },
    swagger: {
        apiDocs: {
            url: 'v3/api-docs',
            method: 'get'
        },
    },
    task: {
        getOne: {
            url: 'Task/${id}',
            method: 'get'
        },
        getAll: {
            url: 'Task/findAll',
            method: 'get'
        },
        add: {
            url: 'Task',
            method: 'post'
        },
        update: {
            url: 'Task',
            method: 'put'
        },
        delete: {
            url: 'Task/${id}',
            method: 'delete'
        },
        start: {
            url: 'Task/${id}/start',
            method: 'get'
        },
        stop: {
            url: 'Task/${id}/stop',
            method: 'get'
        },
        pause: {
            url: 'Task/${id}/pause',
            method: 'get'
        },
        resume: {
            url: 'Task/${id}/resume',
            method: 'get'
        },
        test: {
            url: 'Task/test',
            method: 'post'
        }
    },
    taskFar: {
        getOne: {
            url: 'TaskFar/${id}',
            method: 'get'
        },
        getAll: {
            url: 'TaskFar/findAll',
            method: 'get'
        },
        add: {
            url: 'TaskFar',
            method: 'post'
        },
        update: {
            url: 'TaskFar',
            method: 'put'
        },
        delete: {
            url: 'TaskFar/${id}',
            method: 'delete'
        },
        start: {
            url: 'TaskFar/${id}/start',
            method: 'get'
        },
        stop: {
            url: 'TaskFar/${id}/stop',
            method: 'get'
        },
        pause: {
            url: 'TaskFar/${id}/pause',
            method: 'get'
        },
        resume: {
            url: 'TaskFar/${id}/resume',
            method: 'get'
        },
        test: {
            url: 'TaskFar/test',
            method: 'post'
        }
    },
    proxy: {
        getOne: {
            url: 'Proxy/${id}',
            method: 'get'
        },
        getAll: {
            url: 'Proxy/findAll',
            method: 'get'
        },
        add: {
            url: 'Proxy',
            method: 'post'
        },
        update: {
            url: 'Proxy',
            method: 'put'
        },
        delete: {
            url: 'Proxy/${id}',
            method: 'delete'
        }
    },
    config: {
        getOne: {
            url: 'Config/${id}',
            method: 'get'
        },
        getAll: {
            url: 'Config/findAll',
            method: 'get'
        },
        add: {
            url: 'Config',
            method: 'post'
        },
        update: {
            url: 'Config',
            method: 'put'
        },
        delete: {
            url: 'Config/${id}',
            method: 'delete'
        }
    },
    insCrack: {
        getOne: {
            url: 'Crack/${id}',
            method: 'get'
        },
        getAll: {
            url: 'Crack/findAll',
            method: 'get'
        },
        add: {
            url: 'Crack',
            method: 'post'
        },
        update: {
            url: 'Crack',
            method: 'put'
        },
        delete: {
            url: 'Crack/${id}',
            method: 'delete'
        }
    },
    user: {
        getOne: {
            url: 'User/${id}',
            method: 'get'
        },
        getAll: {
            url: 'User/findAll',
            method: 'get'
        },
        add: {
            url: 'User',
            method: 'post'
        },
        update: {
            url: 'User',
            method: 'put'
        },
        delete: {
            url: 'User/${id}',
            method: 'delete'
        }
    },
    notifyEmail: {
        getOne: {
            url: 'NotifyEmail/${id}',
            method: 'get'
        },
        getAll: {
            url: 'NotifyEmail/findAll',
            method: 'get'
        },
        add: {
            url: 'NotifyEmail',
            method: 'post'
        },
        update: {
            url: 'NotifyEmail',
            method: 'put'
        },
        delete: {
            url: 'NotifyEmail/${id}',
            method: 'delete'
        }
    },
    eMail: {
        send: {
            url: 'Email',
            method: 'post',
            param_example: {content: "发送一封邮件", title: "swagger测试", to: "ni81@qq.com"},
        },
    },
    ins: {
        count: {
            url: 'Ins/user/count',
            method: 'get'
        },
        current: {
            url: 'Ins/user/current',
            method: 'get'
        },
        move: {
            url: 'Ins/user/move',
            method: 'get'
        },
        next: {
            url: 'Ins/user/next',
            method: 'get'
        },
        reset: {
            url: 'Ins/user/reset',
            method: 'get'
        },
    },
    post: {
        getOne: {
            url: 'Ins/post/${id}',
            method: 'get'
        },
        getPage: {
            url: 'Ins/post',
            method: 'get'
        },
        addOrUpdate: {
            url: 'Ins/post',
            method: 'post'
        },
        delete: {
            url: 'Ins/post/${id}',
            method: 'delete'
        },
        deleteMany: {
            url: 'Ins/post/delete',
            method: 'delete'
        },
        count: {
            url: 'Ins/post/count',
            method: 'get'
        },
    },
    middle: {
        heartBeat: {
            url: 'middle/ping',
            method: 'get'
        },
        lastHeartBeat: {
            url: 'middle/lastHeart',
            method: 'get'
        },
    },
    file: {
        listJson: {
            url: 'file/json',
            method: 'get'
        },
        list: {
            url: 'file',
            method: 'get'
        },
        delete: {
            // file?del=<String fileName>
            url: 'file?del=${file}',
            method: 'get',
            param_example: {file: 'aaa.txt0.8008880603584201'}
        },
        getOne: {
            // file?get=<String fileName>
            url: 'file?get=${file}',
            method: 'get',
            param_example: {file: 'aaa.txt0.8008880603584201'},
            config: {
                responseType: 'blob'
            },
        },
        upload: {
            url: 'file',
            method: 'post',
            param_example: '/*FormData*/tmp=await api.file.getOne({file:\'aaa.jpg\'});fd=new FormData();fd.append(\'file\',tmp.data,\'test.jpg\'+Math.random());up=await api.file.upload(fd)',
            config: {
                overrideMimeType: 'multipart/form-data'
            },
            info: '参数格式为 FormData'
        },
    },
    gram: {
        // https://www.instagram.com/graphql/query/?query_id=17888483320059182&id=1001283596&first=40
        getPost: {
            url: 'https://www.instagram.com/graphql/query/?query_id=17888483320059182&id=${id}&first=40',
            method: 'get',
            param_example: {id: '1001283596'}
        },
        // https://www.instagram.com/graphql/query/?query_hash=8c2a529969ee035a5063f2fc8602a0fd&variables={"id":"51132914782","first":12}
        getPostNew: {
            url: 'https://www.instagram.com/graphql/query/?query_hash=${queryHash}&variables={"id":"${id}","first":40}',
            method: 'get',
            queryHash: true,
            // 多图id: '51132914782'
            // 多图__typename: 'GraphSidecar'
            param_example: {id: '1001283596'}
        },
        getJpeg: {
            url: 'http${url}',
            method: 'get',
            param_example: {url: '1001283596'},
            usage_example: 'await api.gram.getJpeg({url:decodeURIComponent(\'https://scontent-hkt1-1.cdninstagram.com/v/t51.2885-15/sh0.08/e35/c0.180.1440.1440a/s640x640/271796139_918243432220319_6606270248120166088_n.jpg?_nc_ht=scontent-hkt1-1.cdninstagram.com\u0026_nc_cat=111\u0026_nc_ohc=2KPbEtGsfIIAX9MVvN3\u0026edm=APU89FABAAAA\u0026ccb=7-4\u0026oh=00_AT9LlzKJwZxDGjaZyEI4ov4XEGonwuvdU0xr2oAZ4kCgWA\u0026oe=61EC5D1C\u0026_nc_sid=86f79a\').substr(4)})',
            config: {
                responseType: 'blob'
            },
        },
    },
    far: {
        getTest: {
            url: '/hk/shopping/women/dion-lee-corset-detail-minidress-item-17005926.aspx',
            method: 'get',
            example: `.between('content="',' minidress',true)==='Shop Dion Lee corset-detail'`
        },
    },
    mongo: {
        save: {
            url: 'Mongo/post/${name}',
            method: 'post',
            example: `{id: '1001283596'}`,
            param_example: {id: '1001283596', name: 'test'}
        },
        saveMany: {
            url: 'Mongo/post/${name}/many',
            method: 'post',
            example: `{id: '1001283596'}`,
            param_example: {id: '1001283596', name: 'test'}
        },
        updateMany: {
            url: 'Mongo/post/${name}/many',
            method: 'put',
            example: `{condition: {id: '1001283596'}, update: {id: '1001283596'}}`,
            param_example: {condition: {id: '1001283596'}, update: {id: '1001283596', name: 'test'}}
        },
        delete: {
            url: 'Mongo/post/${name}/${id}',
            method: 'delete',
            param_example: {name: 'test', id: '1001283596'}
        },
        findOne: {
            url: '/Mongo/post/${name}/${id}',
            method: 'get',
            param_example: {
                name: 'test', id: '1001283596'
            }
        },
        find: {
            url: 'Mongo/post/${name}/${start}/${size}',
            method: 'post',
            param_example: {
                name: 'test', start: 0, size: 10,
                id: '1001283596'
            }
        },
        count: {
            url: 'Mongo/post/${name}/count',
            method: 'get',
            param_example: {name: 'test'}
        },
        countCondition: {
            url: 'Mongo/post/${name}/count',
            method: 'post',
            param_example: {name: 'test', id: '1001283596'}
        },
    }
};

let doBuildUrl = (apiCfg, params) => {
    let url = apiCfg.url.startsWith('http') ? apiCfg.url : appConfig.host + apiCfg.url;
    let pathParams = url.match(/\${\w+}/g);
    if (pathParams) {
        pathParams.forEach(param => {
            let paramKey = param.match(/\${(.*?)}/)[1];
            let paramValue = params[paramKey] || '';
            // 数组参数使用方式: 第一条数据作为路径参数, 第二条数据作为参数; 例如: /api/post/1001283596/many
            // 没有路径参数的话, 全部作为参数; 例如: /api/post/many
            if (Array.isArray(params) && params.length > 0) {
                let paramObj = params[0];
                paramValue = paramObj[paramKey] || '';
                delete paramObj[paramKey];
            }
            if (apiCfg.queryHash && 'queryHash' === paramKey && !paramValue) {
                paramValue = store.state.queryHash;
            }
            url = url.replace(param, paramValue);
            delete params[paramKey];
        })
    }
    return url;
};

/**
 * build for axios
 * @param apiCfg
 * @returns {(function(*=): *)|*}
 */
let build = (apiCfg) => {
    if (apiCfg) {
        let keys = Object.keys(apiCfg);
        if (keys.includes('url') && apiCfg.url) {
            let method = apiCfg.method || 'get';
            axios[method].toString();
            //try {
            //} catch (e) {
            //    throw new Error('错误的方法名! method: ' + method + ' url: ' + apiCfg.url);
            //}
            return (originParams = {}) => {
                let params = deepClone(originParams);
                let url = doBuildUrl(apiCfg, params);
                // 支持配置信息(如文件下载 blob)
                if (apiCfg.config) {
                    Object.assign(params, apiCfg.config)
                }
                // 支持表单提交(如文件上传 FormData)
                if (typeof (originParams) && originParams instanceof FormData) {
                    if (appConfig.isDebug) {
                        console.log('url', url, extractFormData(originParams), params, apiCfg);
                    }
                    return axios[method](url, originParams, params);
                }
                if (appConfig.isDebug) {
                    console.log('url', url, extractFormData(params), apiCfg);
                }
                // 数组参数使用方式: 第一条数据作为路径参数, 第二条数据作为参数; 例如: /api/post/1001283596/many
                // 没有路径参数的话, 全部作为参数; 例如: /api/post/many
                if (Array.isArray(params) && params.length > 0) {
                    params = params[1] ? params[1] : [];
                }
                return axios[method](url, params);
            }
        } else {
            keys.forEach(key => apiCfg[key] = build(apiCfg[key]));
            return apiCfg;
        }
    }
};

/**
 * build for GM_xmlhttpRequest
 * @param apiCfg
 * @returns {(function(*=): *)|*}
 */
let buildGm = (apiCfg) => {
    if (apiCfg) {
        let keys = Object.keys(apiCfg);
        if (keys.includes('url') && apiCfg.url) {
            let method = apiCfg.method || 'get';
            return (params = {}) => {
                params = deepClone(params);
                let url = doBuildUrl(apiCfg, params);
                // 数组参数使用方式: 第一条数据作为路径参数, 第二条数据作为参数; 例如: /api/post/1001283596/many
                // 没有路径参数的话, 全部作为参数; 例如: /api/post/many
                if (Array.isArray(params) && params.length > 0) {
                    params = params[1] ? params[1] : [];
                }
                let param = {
                    url,
                    method,
                    data: JSON.stringify(params),
                };
                console.log('url', url, params, apiCfg, param);
                window.GM_xmlhttpRequest = typeof (GM_xmlhttpRequest) === 'undefined' ? undefined : GM_xmlhttpRequest;
                return new Promise((resolve, reject) => {
                    param.onerror = (error) => {
                        reject(error)
                    };
                    param.onload = (resp) => {
                        let headers = {};
                        let headArr = resp.responseHeaders.split("\n");
                        headArr.forEach(head => {
                            let hArr = head.split(";");
                            let key = hArr.shift();
                            headers[key] = hArr.join().trim();
                        });
                        resp.headers = headers;
                        let data = {};
                        if (isJson(resp.responseText)) {
                            data = JSON.parse(resp.responseText);
                        }
                        resp.data = data;
                        resolve(resp);
                    };
                    if (!!GM_xmlhttpRequest) {
                        GM_xmlhttpRequest(param)
                    } else {
                        console.error("没有找到GM_xmlhttpRequest");
                    }
                });
            }
        } else {
            keys.forEach(key => apiCfg[key] = buildGm(apiCfg[key]));
            return apiCfg;
        }
    }
};

/**
 * usage in tamperMonkey:
 * let gmDownFile = buildGmDownFile();
 *
 * @returns {function(String): Promise<Object>}
 * @see <a href="https://www.tampermonkey.net/documentation.php#GM_xmlhttpRequest">tamperMonkey doc</a>
 * @author http://mmbro.gitee.com
 * @since 2022110
 */
let buildGmDownFile = () => {
    window.GM_xmlhttpRequest = typeof (GM_xmlhttpRequest) === 'undefined' ? undefined : GM_xmlhttpRequest;
    return (url) => {
        url = String(url).startsWith('http') ? url : appConfig.host + url;
        return new Promise((resolve, reject) => {
            let param = {
                url,
                method: 'get',
                responseType: 'blob'
            };
            param.onerror = (error) => {
                reject(error)
            };
            param.onload = (resp) => {
                resp.data = resp.response;
                resolve(resp);
            };
            if (!!GM_xmlhttpRequest) {
                GM_xmlhttpRequest(param)
            } else {
                console.error("没有找到GM_xmlhttpRequest");
            }
        })
    }
};

/**
 * usage in tamperMonkey:
 * let gmUpLoad = buildGmUpLoadFile();
 *
 * @returns {function(String, FormData): Promise<Object>}
 * @see <a href="https://www.tampermonkey.net/documentation.php#GM_xmlhttpRequest">tamperMonkey doc</a>
 * @author http://mmbro.gitee.com
 * @since 2022110
 */
let buildGmUpLoadFile = () => {
    window.GM_xmlhttpRequest = typeof (GM_xmlhttpRequest) === 'undefined' ? undefined : GM_xmlhttpRequest;
    return (url, formData) => {
        url = String(url).startsWith('http') ? url : appConfig.host + url;
        return new Promise((resolve, reject) => {
            let param = {
                url,
                data: formData,
                method: 'post',
                responseType: 'blob',
                overrideMimeType: 'multipart/form-data',
            };
            if (typeof (formData) === 'undefined') {
                reject('error! formData is not a FormData Object!')
            }
            param.onerror = (error) => {
                reject(error)
            };
            let isHeadersJson = (headers) => headers && headers['content-type'] && String(headers['content-type']).indexOf('application/json') >= 0;
            param.onload = (resp) => {
                let extractRespHeaders = (responseHeaders) => {
                    let headers = {};
                    let headArr = responseHeaders.split("\n");
                    headArr.forEach(head => {
                        if (head) {
                            let hArr = head.split(":");
                            let key = hArr.shift();
                            headers[key] = hArr.join().trim();
                        }
                    });
                    return headers;
                };
                resp.headers = extractRespHeaders(resp.responseHeaders);
                let data;
                if (isHeadersJson(headers)) {
                    if (isJson(resp.responseText)) {
                        data = JSON.parse(resp.responseText);
                    } else {
                        if (appConfig.isDebug) {
                            let contentType = headers['content-type'];
                            console.error('响应非json文本,contentType: %s', contentType);
                        }
                        data = resp.responseText;
                    }
                } else {
                    data = resp.responseText;
                }
                resp.data = data;
                resp.request = param;
                resolve(resp);
            };
            if (!!GM_xmlhttpRequest) {
                GM_xmlhttpRequest(param)
            } else {
                console.error("没有找到GM_xmlhttpRequest");
            }
        })
    }
};